using System;
using System.Collections.Generic;
using System.Text;

using TTDotNet.Util;
using TTDotNet.Engine;
using TTDotNet.Engine.Terrain;
using TTDotNet.Engine.ThreeDEngine;
using System.Drawing;
using TTDotNet.Engine.Networks;

namespace TTDotNet.Engine.Create.Tools
{
    public class BuildRoadTool : Tool
    {
        private DDDCoordinate dragStart;
        private RotateType dragStartSide;

        private Coordinate highlighted = Coordinate.Invalid;

        private List<CoordSide> hightlightedSides = new List<CoordSide>();

        public BuildRoadTool(Game game) : base(game, ToolType.Road)
        {
            toolType = ToolType.Road;
            dragStart = new DDDCoordinate(-1, -1, -1);
            dragStartSide = RotateType.Invalid;
        }

        public override void OnDeactivated()
        {
            if(!highlighted.IsInvalid)
                Game.gameWorld.Map.UpdateHighlight(highlighted.X, highlighted.Z, HighlightType.NoHighlight, RotateType.Zero);
        }

        public override void MouseMove(Point mouseLocation, GridPickData data)
        {
            foreach (CoordSide side in hightlightedSides)
            {
                Game.gameWorld.Map.UpdateHighlight(side.Coordinate.X, side.Coordinate.Z, HighlightType.NoHighlight, RotateType.Zero);
            }

            if (!highlighted.IsInvalid)
                Game.gameWorld.Map.UpdateHighlight(highlighted.X, highlighted.Z, HighlightType.NoHighlight, RotateType.Zero);

            if (!data.Coord.IsInvalid)
            {
                Game.gameWorld.Map.UpdateHighlight(data.Coord.X, data.Coord.Z, HighlightType.Half, data.TileSide);
            }
            highlighted.X = data.Coord.X;
            highlighted.Z = data.Coord.Z;

            

            try
            {
                if (!dragStart.IsInvalid && !data.Coord.IsInvalid)
                {
                    RoadStrip strip = GetRoadStrip(dragStart, dragStartSide,
                                        new DDDCoordinate(data.Coord.X, Game.gameWorld.Map.GetTileHeight(data.Coord), data.Coord.Z), data.TileSide);

                    if (strip.coords.Count > 4)
                    {

                    }

                    List<CoordSide> coordSides = new List<CoordSide>();
                    RotateType type = strip.start;

                    DDDCoordinate last = new DDDCoordinate(-1, -1, -1);

                    foreach (DDDCoordinate coord in strip.coords)
                    {
                        if (last.Equals(coord))
                            Game.gameWorld.Map.UpdateHighlight(coord.X, coord.Z, HighlightType.Full, RotateType.Zero);
                        else
                            Game.gameWorld.Map.UpdateHighlight(coord.X, coord.Z, HighlightType.Half, type);

                        coordSides.Add(new CoordSide(coord, type));
                        type = (RotateType)(((int)type + 2) % 4);

                        last = coord;
                    }

                    hightlightedSides = coordSides;
                    
                }

            }
            catch (Exception e)
            {

            }

        }

        public override void MouseDown(Point mouseLocation, GridPickData data)
        {
            if (!Game.paused)
            {
                if (!(data.Coord.X == -1))
                {
                    int clickX = data.Coord.X;
                    int clickZ = data.Coord.Z;

                    Tile sTile = Game.gameWorld.Map.GetTile(data.Coord);
                    DDDCoordinate tileCoord = new DDDCoordinate(sTile.Coordinate, Game.gameWorld.Map.GetTileHeight(clickX, clickZ));

                    dragStart = tileCoord;
                    dragStartSide = data.TileSide;
                }
            }
        }

        public override void MouseUp(Point mouseLocation, GridPickData data)
        {
            if (!Game.paused)
            {
                if (!(data.Coord.X == -1))
                {
                    DDDCoordinate dragFinish = new DDDCoordinate(data.Coord, Game.gameWorld.Map.GetTileHeight(data.Coord));
                    RotateType dragFinishSide = data.TileSide;

                    Networks.RoadStrip added = GetRoadStrip(dragStart, dragStartSide, dragFinish, dragFinishSide);
                    bool roadOK = true;

                    if (added.coords.Count != 0)
                    {
                        RotateType rotType = added.start;

                        foreach (DDDCoordinate roadCoord in added.coords)
                        {
                            if (!(CheckRoadBuild(roadCoord, rotType)))
                            {
                                roadOK = false;
                                break;
                            }
                            rotType = (RotateType)(((int)rotType + 2) % 4);
                        }
                    }
                    else
                    {
                        roadOK = false;
                    }

                    if (roadOK)
                    {
                        Game.gameWorld.RoadNetwork.AddRoad(added);

                        Tile pertile;

                        foreach (DDDCoordinate c in added.coords)
                        {
                            pertile = Game.gameWorld.Map.GetTile(c);
                            pertile.LandType = LandType.Road;
                            pertile.Texture = Game.texLoader.GetTexture(Game.gameWorld.RoadNetwork.GetRoadType(c));
                            pertile.RotateType = Game.gameWorld.RoadNetwork.GetRotateType(c);
                            Game.gameWorld.Map.Grid.UpdateTile(pertile);
                        }
                    }
                }
                dragStart.X = -1;
            }
        }

        private Networks.RoadStrip GetRoadStrip(DDDCoordinate coordFrom, RotateType startSide, DDDCoordinate coordTo, RotateType finishSide)
        {
            if (startSide == RotateType.Invalid || finishSide == RotateType.Invalid) return new RoadStrip(new List<DDDCoordinate>(), RotateType.Invalid);
            if (coordFrom == null || coordTo == null) return new Networks.RoadStrip(new List<DDDCoordinate>(), RotateType.Invalid);

            if (Math.Abs((int)finishSide - (int)startSide) % 2 != 0) return new Networks.RoadStrip(new List<DDDCoordinate>(), RotateType.Invalid);

            List<DDDCoordinate> coordsAdded = new List<DDDCoordinate>();

            RotateType startType = startSide;

            int newX = coordFrom.X;
            int newZ = coordFrom.Z;

            int incX = 0, incZ = 0;

            if (coordFrom.X < coordTo.X)
            {
                incX = 1;
            }
            else if (coordFrom.X > coordTo.X)
            {
                incX = -1;
            }

            if (coordFrom.Z < coordTo.Z)
            {
                incZ = 1;
            }
            else if (coordFrom.Z > coordTo.Z)
            {
                incZ = -1;
            }

            if (incX != 0 && (startSide != RotateType.Ninety && startSide != RotateType.TwoSeventy)) { return new Networks.RoadStrip(new List<DDDCoordinate>(), RotateType.Invalid); }
            if (incZ != 0 && (startSide != RotateType.Zero && startSide != RotateType.OneEighty)) { return new Networks.RoadStrip(new List<DDDCoordinate>(), RotateType.Invalid); }

            if (incX != 0 && incZ != 0) return new Networks.RoadStrip(new List<DDDCoordinate>(), RotateType.Invalid);

            while ((newX != coordTo.X) || (newZ != coordTo.Z) || startType != finishSide)
            {
                coordsAdded.Add(new DDDCoordinate(newX, Game.gameWorld.Map.GetTileHeight(newX, newZ), newZ));

                if (incX == 1 && startType == RotateType.Ninety) { newX++; }
                if (incX == -1 && startType == RotateType.TwoSeventy) { newX--; }
                if (incZ == 1 && startType == RotateType.Zero) { newZ++; }
                if (incZ == -1 && startType == RotateType.OneEighty) { newZ--; }

                startType = (RotateType)(((int)startType + 2) % 4);
            }

            coordsAdded.Add(new DDDCoordinate(coordTo.X, coordTo.Y, coordTo.Z));

            return new Networks.RoadStrip(coordsAdded, startSide);
        }

        private bool CheckRoadBuild(DDDCoordinate coord, RotateType type)
        {
            SlopeType slopeType = Game.gameWorld.Map.GetTileSlopeType(coord.X, coord.Z);

            if(slopeType != SlopeType.Flat && slopeType != SlopeType.FlatSlope) {
                return false;
            }

            return true;
        }

    }

    public struct CoordSide
    {
        private DDDCoordinate coord;
        private RotateType side;

        public CoordSide(DDDCoordinate coord, RotateType type)
        {
            this.coord = coord;
            this.side = type;
        }

        public DDDCoordinate Coordinate
        {
            get { return coord; }
        }

        public RotateType Side
        {
            get { return side; }
        }

    }

    
}
